
// import haxe.ds.Array;
// import oimo.common.MathUtil;
// import oimo.dynamics.constraint.info.joint.JointSolverInfo;
// import oimo.dynamics.constraint.info.joint.JointSolverInfoRow;

import { Boundary } from "./Boundary";
import { BoundaryBuildInfo } from "./BoundaryBuildInfo";
import { JointSolverInfo } from "../../info/joint/JointSolverInfo";
import { JointSolverInfoRow } from "../../info/joint/JointSolverInfoRow";
import { MathUtil } from "../../../../common/MathUtil";

/**
 * numberernal class.
 */
export class BoundaryBuilder {
	numBoundaries: number;
	boundaries: Array<Boundary>;

	maxRows: number;
	bbInfo: BoundaryBuildInfo;

	constructor(maxRows: number) {
		// TODO: O(2^N) is inefficient?
		this.maxRows = maxRows;
		this.numBoundaries = 0;
		this.boundaries = new Array<Boundary>(1 << maxRows);

		this.bbInfo = new BoundaryBuildInfo(maxRows);
	}

	public buildBoundariesRecursive(info: JointSolverInfo, i: number): void {
		if (i == info.numRows) {
			this.addBoundary().init(this.bbInfo);
			return;
		}

		var row: JointSolverInfoRow = info.rows[i];
		var lowerLimitEnabled: boolean = row.minImpulse > MathUtil.NEGATIVE_INFINITY;
		var upperLimitEnabled: boolean = row.maxImpulse < MathUtil.POSITIVE_INFINITY;

		var disabled: boolean = row.minImpulse == 0 && row.maxImpulse == 0;

		if (disabled) {
			// try inactive case
			this.bbInfo.pushBounded(i, 0);
			this.buildBoundariesRecursive(info, i + 1);
			this.bbInfo.popBounded();
			return;
		}

		// try unbounded case
		this.bbInfo.pushUnbounded(i);
		this.buildBoundariesRecursive(info, i + 1);
		this.bbInfo.popUnbounded();

		if (lowerLimitEnabled) {
			// try lower bounded case
			this.bbInfo.pushBounded(i, -1);
			this.buildBoundariesRecursive(info, i + 1);
			this.bbInfo.popBounded();
		}
		if (upperLimitEnabled) {
			// try upper bounded case
			this.bbInfo.pushBounded(i, 1);
			this.buildBoundariesRecursive(info, i + 1);
			this.bbInfo.popBounded();
		}
	}


	private addBoundary(): Boundary {
		if (this.boundaries[this.numBoundaries] == null) {
			this.boundaries[this.numBoundaries] = new Boundary(this.maxRows);
		}
		return this.boundaries[this.numBoundaries++];
	}

	public buildBoundaries(info: JointSolverInfo): void {
		this.numBoundaries = 0;

		this.bbInfo.clear();
		this.buildBoundariesRecursive(info, 0);
	}

}
